package org.jvm.nativemethod.methods.java.util;

import org.jvm.nativemethod.NativeMethodRegister;
import org.jvm.rtda.Object;
import org.jvm.rtda.heap.classLoader.KlassLoaderRegister;
import org.jvm.rtda.thread.*;
import org.jvm.util.JvmUtil;

import java.lang.reflect.*;
import java.util.zip.ZipFile;

/**
 * @author 海燕
 * @date 2023/2/26
 */
public class ZipFileNativeMethod extends NativeMethodRegister {

    public static void init() throws NoSuchMethodException {
        register("java/util/zip/ZipFile", "initIDs", "()V", ZipFileNativeMethod.class.getDeclaredMethod("initIDs", Frame.class));
        register("java/util/zip/ZipFile", "freeEntry", "(JJ)V", ZipFileNativeMethod.class.getDeclaredMethod("freeEntry", Frame.class));
        register("java/util/zip/ZipFile", "getEntry", "(J[BZ)J", ZipFileNativeMethod.class.getDeclaredMethod("getEntry", Frame.class));
        register("java/util/zip/ZipFile", "getEntryBytes", "(JI)[B", ZipFileNativeMethod.class.getDeclaredMethod("getEntryBytes", Frame.class));
        register("java/util/zip/ZipFile", "getEntryCrc", "(J)J", ZipFileNativeMethod.class.getDeclaredMethod("getEntryCrc", Frame.class));
        register("java/util/zip/ZipFile", "getEntryCSize", "(J)J", ZipFileNativeMethod.class.getDeclaredMethod("getEntryCSize", Frame.class));
        register("java/util/zip/ZipFile", "getEntryFlag", "(J)I", ZipFileNativeMethod.class.getDeclaredMethod("getEntryFlag", Frame.class));
        register("java/util/zip/ZipFile", "getEntryMethod", "(J)I", ZipFileNativeMethod.class.getDeclaredMethod("getEntryMethod", Frame.class));
        register("java/util/zip/ZipFile", "getEntrySize", "(J)J", ZipFileNativeMethod.class.getDeclaredMethod("getEntrySize", Frame.class));
        register("java/util/zip/ZipFile", "getEntryTime", "(J)J", ZipFileNativeMethod.class.getDeclaredMethod("getEntryTime", Frame.class));
        register("java/util/zip/ZipFile", "getNextEntry", "(JI)J", ZipFileNativeMethod.class.getDeclaredMethod("getNextEntry", Frame.class));
        register("java/util/zip/ZipFile", "getTotal", "(J)I", ZipFileNativeMethod.class.getDeclaredMethod("getTotal", Frame.class));
        register("java/util/zip/ZipFile", "open", "(Ljava/lang/String;IJZ)J", ZipFileNativeMethod.class.getDeclaredMethod("open", Frame.class));
        register("java/util/zip/ZipFile", "read", "(JJJ[BII)I", ZipFileNativeMethod.class.getDeclaredMethod("read", Frame.class));
        register("java/util/zip/ZipFile", "startsWithLOC", "(J)Z", ZipFileNativeMethod.class.getDeclaredMethod("startsWithLOC", Frame.class));
    }

    //    private static native void initIDs();
    public static void initIDs(Frame frame) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        Method method = ZipFile.class.getDeclaredMethod("initIDs");
        method.setAccessible(true);
        method.invoke(null);
    }

    // private static native long open(String name, int mode, long lastModified, boolean usemmap) throws IOException;
    public static void open(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        Object name = localVars.getRef(0);
        int mode = localVars.getInt(1);
        long lastModified = localVars.getLong(2);
        boolean usemmap = localVars.getInt(4) == 1;

        Method method = ZipFile.class.getDeclaredMethod("open", String.class, int.class, long.class, boolean.class);
        method.setAccessible(true);
        long zipFile = (long) method.invoke(null, JvmUtil.javaStringToJvmString(name), mode, lastModified, usemmap);
        frame.getOperandStack().pushLong(zipFile);
    }

    // private static native void freeEntry(long jzfile, long jzentry);
    public static void freeEntry(Frame frame) {
        //TODO
    }

    // private static native long getEntry(long jzfile, byte[] name, boolean addSlash);
    public static void getEntry(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzfile = localVars.getLong(0);
        byte[] name = (byte[]) localVars.getRef(2).getData();
        boolean addSlash = localVars.getInt(3) == 1;

        Method method = ZipFile.class.getDeclaredMethod("getEntry", long.class, byte[].class, boolean.class);
        method.setAccessible(true);
        long zipFile = (long) method.invoke(null, jzfile, name, addSlash);
        frame.getOperandStack().pushLong(zipFile);
    }

    // private static native byte[] getEntryBytes(long jzentry, int type);
    public static void getEntryBytes(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);
        int type = localVars.getInt(2);

        Method method = ZipFile.class.getDeclaredMethod("getEntryBytes", long.class, int.class);
        method.setAccessible(true);
        byte[] entryBytes = (byte[]) method.invoke(null, jzentry, type);

        if (entryBytes == null) {
            frame.getOperandStack().pushRef(null);
            return;
        }

        Object res = KlassLoaderRegister.getBootKlassLoader().loadKlass("[B").newArray(entryBytes.length);
        res.setData(entryBytes);

        frame.getOperandStack().pushRef(res);
    }

    // private static native long getEntryCrc(long jzentry);
    public static void getEntryCrc(Frame frame) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getEntryCrc", long.class);
        method.setAccessible(true);
        long crc = (long) method.invoke(null, jzentry);

        frame.getOperandStack().pushLong(crc);
    }

    // private static native long getEntryCSize(long jzentry);
    public static void getEntryCSize(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getEntryCSize", long.class);
        method.setAccessible(true);
        long csize = (long) method.invoke(null, jzentry);

        frame.getOperandStack().pushLong(csize);
    }

    // private static native int getEntryFlag(long jzentry);
    public static void getEntryFlag(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getEntryFlag", long.class);
        method.setAccessible(true);
        int entryFlag = (int) method.invoke(null, jzentry);

        frame.getOperandStack().pushInt(entryFlag);
    }

    // private static native int getEntryMethod(long jzentry);
    public static void getEntryMethod(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getEntryMethod", long.class);
        method.setAccessible(true);
        int entryMethod = (int) method.invoke(null, jzentry);

        frame.getOperandStack().pushInt(entryMethod);
    }

    // private static native long getEntrySize(long jzentry);
    public static void getEntrySize(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getEntrySize", long.class);
        method.setAccessible(true);
        long size = (long) method.invoke(null, jzentry);

        frame.getOperandStack().pushLong(size);
    }

    // private static native long getEntryTime(long jzentry);
    public static void getEntryTime(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getEntryTime", long.class);
        method.setAccessible(true);
        long entryTime = (long) method.invoke(null, jzentry);

        frame.getOperandStack().pushLong(entryTime);
    }

    // private static native long getNextEntry(long jzfile, int i);
    public static void getNextEntry(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        LocalVars localVars = frame.getLocalVars();
        long jzfile = localVars.getLong(0);
        int i = localVars.getInt(2);

        Method method = ZipFile.class.getDeclaredMethod("getNextEntry", long.class, int.class);
        method.setAccessible(true);
        long nextEntry = (long) method.invoke(null, jzfile, i);

        frame.getOperandStack().pushLong(nextEntry);
    }

    // private static native int getTotal(long jzfile);
    public static void getTotal(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        LocalVars localVars = frame.getLocalVars();
        long jzentry = localVars.getLong(0);

        Method method = ZipFile.class.getDeclaredMethod("getTotal", long.class);
        method.setAccessible(true);
        int total = (int) method.invoke(null, jzentry);

        frame.getOperandStack().pushInt(total);
    }

    // private static native int read(long jzfile, long jzentry, long pos, byte[] b, int off, int len);
    public static void read(Frame frame) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        LocalVars localVars = frame.getLocalVars();
        long jzfile = localVars.getLong(0);
        long jzentry = localVars.getLong(2);
        long pos = localVars.getLong(4);
        byte[] b = (byte[]) localVars.getRef(6).getData();
        int off = localVars.getInt(7);
        int len = localVars.getInt(8);

        Method method = ZipFile.class.getDeclaredMethod("read", long.class, long.class, long.class, byte[].class, int.class, int.class);
        method.setAccessible(true);
        int res = (int) method.invoke(null, jzfile, jzentry, pos, b, off, len);
        frame.getOperandStack().pushInt(res);
    }

    public static void startsWithLOC(Frame frame) {
        frame.getOperandStack().pushBoolean(true);
    }

}
